File: Argument{T}.cs
Web Access
Project: src\src\command-line-api\src\System.CommandLine\System.CommandLine.csproj (System.CommandLine)
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.Generic;
using System.CommandLine.Parsing;
using System.Diagnostics.CodeAnalysis;
namespace System.CommandLine
{
    /// <inheritdoc cref="Argument" />
    public class Argument<T> : Argument
    {
        private Func<ArgumentResult, T?>? _customParser;
        private Func<ArgumentResult, T>? _defaultValueFactory;

        /// <summary>
        /// Initializes a new instance of the Argument class.
        /// </summary>
        /// <param name="name">The name of the argument. This name can be used to look up the parsed value and is displayed in help.</param>
        public Argument(string name) : base(name)
        {
        }

        /// <summary>
        /// Gets or sets the delegate to invoke to create the default value.
        /// </summary>
        /// <remarks>
        /// This delegate is invoked when there was no parse input provided for given Argument.
        /// The same instance can be set as <see cref="CustomParser"/>. In that case,
        /// the delegate is also invoked when an input was provided.
        /// </remarks>
        public Func<ArgumentResult, T>? DefaultValueFactory
        {
            get
            {
                if (_defaultValueFactory is null)
                {
                    if (this is Argument<bool> boolArgument)
                    {
                        boolArgument.DefaultValueFactory = _ => false;
                    }
                }
                return _defaultValueFactory;
            }
            set => _defaultValueFactory = value;
        }

        /// <summary>
        /// Gets or sets a custom argument parser.
        /// </summary>
        /// <remarks>
        /// The custom parser is invoked when there was parse input provided for a given Argument.
        /// The same instance can be set as <see cref="DefaultValueFactory"/>; in that case,
        /// the delegate is also invoked when no input was provided.
        /// </remarks>
        public Func<ArgumentResult, T?>? CustomParser
        {
            get => _customParser;
            set
            {
                _customParser = value;

                if (value is not null)
                {
                    ConvertArguments = (ArgumentResult argumentResult, out object? parsedValue) =>
                    {
                        int errorsBefore = argumentResult.SymbolResultTree.ErrorCount;
                        var result = value(argumentResult);

                        if (errorsBefore == argumentResult.SymbolResultTree.ErrorCount)
                        {
                            parsedValue = result;
                            return true;
                        }
                        else
                        {
                            parsedValue = default(T)!;
                            return false;
                        }
                    };
                }
                else
                {
                    ConvertArguments = null;
                }
            }
        }

        /// <inheritdoc />
        public override Type ValueType => typeof(T);

        /// <inheritdoc />
        public override bool HasDefaultValue => DefaultValueFactory is not null;

        internal override object? GetDefaultValue(ArgumentResult argumentResult)
        {
            if (DefaultValueFactory is null)
            {
                throw new InvalidOperationException($"Argument \"{Name}\" does not have a default value");
            }

            return DefaultValueFactory.Invoke(argumentResult);
        }

        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050", Justification = "https://github.com/dotnet/command-line-api/issues/1638")]
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091", Justification = "https://github.com/dotnet/command-line-api/issues/1638")]
        internal static T? CreateDefaultValue()
        {
            if (default(T) is null && typeof(T) != typeof(string))
            {
#if NET7_0_OR_GREATER
                if (typeof(T).IsSZArray)
#else
                if (typeof(T).IsArray && typeof(T).GetArrayRank() == 1)
#endif
                {
                    return (T?)(object)Array.CreateInstance(typeof(T).GetElementType()!, 0);
                }
                else if (typeof(T).IsConstructedGenericType)
                {
                    var genericTypeDefinition = typeof(T).GetGenericTypeDefinition();

                    if (genericTypeDefinition == typeof(IEnumerable<>) ||
                        genericTypeDefinition == typeof(IList<>) ||
                        genericTypeDefinition == typeof(ICollection<>))
                    {
                        return (T?)(object)Array.CreateInstance(typeof(T).GenericTypeArguments[0], 0);
                    }

                    if (genericTypeDefinition == typeof(List<>))
                    {
                        return Activator.CreateInstance<T>();
                    }
                }
            }

            return default;
        }
    }
}